home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v17n02 / turtle.exe / XCI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-03  |  14.2 KB  |  593 lines

  1. /**********************************************************
  2.  *                                                        *
  3.  * XCI.C     An extensible command interpreter for the    *
  4.  * Phar Lap 286 DOS Extender                              *
  5.  *                                                        *
  6.  * Al Williams                                            *
  7.  *                                                        *
  8.  **********************************************************/
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <malloc.h>
  14. #include <dos.h>
  15. #include <phapi.h>
  16. #include <setjmp.h>
  17. #include "xci.h"
  18.  
  19. /* Table of commands (dynamically allocated) */
  20. static struct cmdtbl
  21.   {
  22.   char far *cmd;
  23.   XCICMDP f;
  24.   } *cmds=NULL;
  25.  
  26. /* Number of commands in table */
  27. static unsigned int nrcmds=0;
  28.  
  29. /* Case sensitive? */
  30. static int truecase=0;
  31.  
  32. /* default hook function prototypes */
  33. void xci_prompter();     /* func to prompt */
  34. char *xci_input();       /* func to get input */
  35. void xci_preposthelp();  /* pre & post help command */
  36.  
  37. /* default prompt string -- can be changed by client */
  38. char *xci_prompt="? ";
  39.  
  40. /* default routines -- can be changed by client */
  41. void (*xcif_prompt)()=xci_prompter;
  42. void (*xcif_prehelp)()=xci_preposthelp;
  43. void (*xcif_posthelp)()=xci_preposthelp;
  44. char *(*xcif_input)()=xci_input;
  45.  
  46. /* flag set when break detected */
  47. static int broke;
  48.  
  49. /* Jump to top level command loop */
  50. jmp_buf cmdloop;
  51.  
  52.  
  53. /* default command function prototypes */
  54. XCICMD dofunc(int cmd,char *s,struct udata *data);
  55. XCICMD linkfunc(int cmd,char *s,struct udata *data);
  56. XCICMD quitfunc(int cmd,char *s,struct udata *data);
  57. XCICMD helpfunc(int cmd,char *s,struct udata *data);
  58.  
  59. /* default commands (client must enable goto if desired) */
  60. static char *defcmd[]= { "quit", "help", "link", "do" };
  61.  
  62. /* addresses of default commands */
  63. static XCICMDP deffunc[]={quitfunc,helpfunc,linkfunc,dofunc};
  64.  
  65. /* non-zero if running a script via DO */
  66. static int interactive=0;
  67.  
  68. /* stack of file positions for nested DO commands */
  69. /* Files are closed and reopened to avoid DOS file limit */
  70. static struct fstack
  71.        {
  72.        char *fp;             /* file name */
  73.        long pos;             /* position in file */
  74.        struct fstack * next; /* next fstack record */
  75.        } *instack;
  76.  
  77.  
  78. /* default stdin handle */
  79. static FILE *baseio;
  80.  
  81. /* Current input file */
  82. FILE *xci_infile;
  83.  
  84. /* Set to 1 when someone wants to exit */
  85. int xci_exitflag=0;
  86.  
  87. /* Default break action */
  88. int xci_defaultbrk=1;
  89.  
  90. /* Break vectors */
  91. PIHANDLER oldbreak;
  92. REALPTR oldbreal;
  93. PIHANDLER old1b;
  94. REALPTR old1breal;
  95.  
  96. /* Bios segment (you can't call DosGetBIOSseg from ISR) */
  97. USHORT biosseg;
  98.  
  99. /* ^Break handlers */
  100. void _interrupt _far xci_int1b(REGS16 r)
  101.   {
  102.   union REGS rr;
  103.   unsigned int *keyhead,*keytail;
  104.   if (!xci_defaultbrk)
  105.     {
  106. /* Chain to old break handler (never returns) */
  107.     DosChainToRealIntr(old1breal);
  108.     }
  109.   keyhead=MAKEP(biosseg,0x1A);
  110.   keytail=MAKEP(biosseg,0x1C);
  111.   broke=1;
  112. /* purge keyboard buffer */
  113.   *keyhead=*keytail;
  114. /* push ^C at head */
  115.   rr.h.ah=5;
  116.   rr.x.cx=3;
  117.   int86(0x16,&rr,&rr);
  118.   }
  119.  
  120. void _interrupt _far xci_int16(REGS16 r)
  121.   {
  122.   REGS16 r1;
  123.   unsigned ah=r.ax>>8;
  124.   _enable();
  125.   if (xci_defaultbrk&&(ah==0||ah==0x10||ah==1||ah==0x11))
  126.     {
  127.     do
  128.       {
  129.       r1.ax=0x100;
  130.       r1.flags=0;
  131. /* Simulate interrupt to old INT 16H handler */
  132.       DosRealFarCall(oldbreal,&r1,0,-1,r1.flags);
  133.       if ((r1.flags&64)&&(ah==1||ah==0x11))
  134.         {
  135.         r.flags=r1.flags;
  136.         return;
  137.         }
  138.       } while (r1.flags&64);
  139. /* If break character -- replace it with a carriage return */
  140.       if ((r1.ax&0xff)==3||r1.ax==0x300)
  141.         {
  142.         unsigned int *keyhead;
  143.         keyhead=MAKEP(biosseg,0x1A);
  144.         keyhead=MAKEP(biosseg,*keyhead);
  145.         *keyhead='\r';
  146.         broke=1;
  147.         }
  148.      }
  149.   DosChainToRealIntr(oldbreal);
  150.   }
  151.  
  152.  
  153. /* XCI Clean up */
  154. /* Note: DosExitList requires this to be a pascal function */
  155. void pascal far xci_clean(unsigned int reason)
  156.   {
  157. /* restore interrupt vectors */
  158.    DosSetRealProtVec(0x16,oldbreak,oldbreal,NULL,NULL);
  159.    DosSetRealProtVec(0x1b,old1b,old1breal,NULL,NULL);
  160. /* Exit handler must call DosExitList with EXLST_EXIT
  161.    to proceed with the termination */
  162.    DosExitList(EXLST_EXIT,NULL);
  163.   }
  164.  
  165.  
  166.  
  167. /* default functions */
  168. void xci_prompter(char *s)
  169.    {
  170.    printf("%s",s);
  171.    }
  172.  
  173. char *xci_input(char *inbuf,unsigned int siz,FILE *input)
  174.    {
  175.    return fgets(inbuf,siz,input);
  176.    }
  177.  
  178. void xci_preposthelp()
  179.    {
  180.    }
  181.  
  182.  
  183. /* Main command routine */
  184. /* dll is initial DLL to load
  185.    startfile is initial file to DO
  186.    cases is 1 if case sensitivity is required
  187.    userfunc is pointer to user function called at
  188.             start and end */
  189. int command(char *dll, char *startfile, int cases,
  190.             void far *user,XCICMDP userfunc)
  191.   {
  192.   int i;
  193.   char inbuf[129],*p;
  194.   if (!cmds)
  195.     {
  196. /* first time  (not done for recursive calls) */
  197.    DosGetBIOSSeg(&biosseg);
  198. /* Due to a bug in versions prior to 1.4, you must set
  199.    the INT 16H ProtVec before using PassToProtVec... */
  200.     DosSetProtVec(0x16,xci_int16,&oldbreak);
  201.     DosSetPassToProtVec(0x16,xci_int16,NULL,&oldbreal);
  202.     DosSetPassToProtVec(0x1b,xci_int1b,&old1b,&old1breal);
  203. /* set up exit handler */
  204.     DosExitList(EXLST_ADD,xci_clean);
  205.     truecase=cases;
  206.     xci_infile=stdin;
  207.  
  208. /* install default commands */
  209.     cmds=(struct cmdtbl *)malloc(4*sizeof(struct cmdtbl));
  210.     if (!cmds) return 1;
  211.     nrcmds=4;
  212.     for (i=0;i<nrcmds;i++)
  213.       {
  214.       cmds[i].cmd=defcmd[i];
  215.       cmds[i].f=deffunc[i];
  216.       }
  217.  
  218. /* load default DLL (if specified) */
  219.     if (dll&&*dll)
  220.       if (adddll(dll))
  221.          printf(
  222.            "Warning: unable to load default command DLL\n");
  223.  
  224. /* call user function */
  225.     if (userfunc) userfunc(0,NULL,user);
  226.  
  227. /* execute default DO file */
  228.     if (startfile&&*startfile) dofunc(0,startfile,user);
  229.  
  230. /* set jump buffer for future longjmp's */
  231.     setjmp(cmdloop);
  232.     }
  233.  
  234. /* initilization done -- begin main processing */
  235.   while (1)
  236.     {
  237.     char *token,*tail;
  238. /* if someone wants to quit then quit */
  239.     if (xci_exitflag)
  240.       {
  241. /* call user function */
  242.       if (userfunc) userfunc(1,NULL,user);
  243. /* reset some things in case we are called again */
  244. /* restore interrupt vectors */
  245.       DosSetRealProtVec(0x16,oldbreak,oldbreal,NULL,NULL);
  246.       DosSetRealProtVec(0x1b,old1b,old1breal,NULL,NULL);
  247.       DosExitList(EXLST_REMOVE,xci_clean);
  248.       xci_infile=stdin;
  249.       interactive=0;
  250.       instack=NULL;
  251.       free((void *)cmds);
  252.       cmds=NULL;
  253.       return 0;
  254.       }
  255.  
  256. /* If interactive then prompt */
  257.     if (!interactive) (*xcif_prompt)(xci_prompt);
  258.  
  259. /* get input from user or file */
  260.     *inbuf='\0';
  261.     (*xcif_input)(inbuf,sizeof(inbuf),xci_infile);
  262.  
  263. /* If break detected then go to top level */
  264.     if (broke)
  265.       {
  266.       struct fstack *f;
  267.       broke=0;
  268.       /* free fstack entries */
  269.       for (f=instack;f;f=f->next) free(f->fp);
  270.       instack=NULL;
  271.       interactive=0;
  272.       xci_infile=stdin;
  273.       longjmp(cmdloop,1);
  274.       }
  275.  
  276. /* If end of do file, return. If end of console, ignore */
  277.     if (!*inbuf&&feof(xci_infile))
  278.       {
  279.       if (interactive)
  280.         {
  281.         return 0;
  282.         }
  283.       clearerr(xci_infile);
  284.       continue;
  285.       }
  286.  
  287. /* got some input -- lets look at it */
  288.     i=strspn(inbuf," \t");
  289.  
  290. /* skip blank lines and comments */
  291.     if (inbuf[i]=='\n') continue;
  292.     if (inbuf[i]=='#') continue;
  293.  
  294. /* eat off \n from line */
  295.     p=strchr(inbuf+i,'\n');
  296.     if (p) *p='\0';
  297.  
  298. /* get a token */
  299.     token=strtok(inbuf+i," \t");
  300.     if (!token) continue;  /* this should never happen */
  301.  
  302. /* do we recognize the command? */
  303.     i=findcmd(token);
  304. /* NO: error */
  305.     if (i==-1)
  306.       {
  307.       printf("Unknown command %s\n",token);
  308.       continue;
  309.       }
  310.  
  311. /* YES: compute command's tail (arguments) */
  312.     tail=token+strlen(token)+1;
  313.     tail+=strspn(tail," \t");
  314. /* execute command */
  315.     cmds[i].f(0,tail,user);
  316.     }
  317.   }
  318.  
  319.  
  320. /* Find a command -- search backwards so new commands
  321.    replace old ones */
  322. static int findcmd(char *s)
  323.   {
  324.   int i,stat;
  325.   for (i=nrcmds-1;i>=0;i--)
  326.     {
  327.     if (!(truecase?
  328.            strcmp(s,cmds[i].cmd)
  329.            :
  330.            stricmp(s,cmds[i].cmd)))
  331.        return i;
  332.     }
  333.   return -1;
  334.   }
  335.  
  336.  
  337. /* Add a DLL to the command input table
  338.    returns 0 if successful  */
  339. static adddll(char *dll)
  340.   {
  341.   char cmdnam[33],*p;
  342.   HMODULE h=0;
  343.   unsigned ord=0;
  344.   p=strrchr(dll,'\\');
  345.  
  346. /* check to see if module is already loaded */
  347.   if (!DosGetModHandle(p?p+1:dll,&h))
  348.     {
  349.     printf("%s already loaded\n",p?p+1:dll);
  350.     return 1;
  351.     }
  352.  
  353. /* Load module if possible */
  354.   if (DosLoadModule(0,0,dll,&h))
  355.        return 1;
  356.  
  357. /* find all exported functions in module */
  358.   while (!DosEnumProc(h,cmdnam,&ord))
  359.      {
  360.      PFN fn;
  361. /* Get function's address */
  362.      DosGetProcAddr(h,cmdnam,&fn);
  363. /* add command -- skipt 1st character (it is a _) */
  364.      if (addcmd(cmdnam+1,(XCICMDP) fn)) return 1;
  365.      }
  366.   return 0;
  367.   }
  368.  
  369. /* add a command -- returns 0 for success */
  370. addcmd(char *cmdnam,XCICMDP fn)
  371.   {
  372.   struct cmdtbl *ct;
  373. /* make more room in table  */
  374.   ct=(struct cmdtbl *)
  375.       realloc(cmds,(nrcmds+1)*sizeof(struct cmdtbl));
  376.   if (!ct) return 1;
  377.   cmds=ct;
  378. /* add name and function */
  379.   cmds[nrcmds].cmd=strdup(cmdnam);
  380.   if (!cmds[nrcmds].cmd) return 1;
  381.   cmds[nrcmds++].f=(XCICMDP) fn;
  382.   return 0;
  383.   }
  384.  
  385. /* currently executing file name */
  386. static char curfile[67];
  387.  
  388. /* Command to transfer execution from one file to another
  389.    Only works from inside a file, and must be enabled by
  390.    client program: addcmd("GOTO",gotocmd); */
  391. XCICMD gotofunc(int cmd,char *s,struct udata *data)
  392.   {
  393.   FILE *f;
  394.   if (cmd==2)
  395.     {
  396.     printf("Execute commands from an ASCII file\n");
  397.     return;
  398.     }
  399.   if (cmd==1||!s||!*s)
  400.     {
  401.     printf("goto executes commands from an ASCII file\n"
  402.            "Usage: goto FILENAME\n");
  403.     return;
  404.     }
  405. /* open file */
  406.   f=fopen(s,"r");
  407.   if (!f)
  408.     {
  409.     printf("Can't open %s\n",s);
  410.     perror(s);
  411.     return;
  412.     }
  413.   if (!interactive)
  414.     {
  415.     printf("Use goto only from command files\n"
  416.            "Use do to execute a file\n");
  417.     return;
  418.     }
  419. /* register as current file */
  420.   strcpy(curfile,s);
  421.   fclose(xci_infile);
  422.   xci_infile=f;
  423.   }
  424.  
  425. /* Do a command file */
  426. XCICMD dofunc(int cmd,char *s,struct udata *data)
  427.   {
  428.   FILE *ifile;
  429.   struct fstack recall;
  430.   if (cmd==2)
  431.     {
  432.     printf("Do commands from an ASCII file\n");
  433.     return;
  434.     }
  435.   if (cmd==1||!s||!*s)
  436.     {
  437.     printf("Do executes commands from an ASCII file\n"
  438.            "Usage: do FILENAME\n");
  439.     return;
  440.     }
  441.  
  442. /* open file */
  443.   ifile=fopen(s,"r");
  444.   if (!ifile)
  445.     {
  446.     printf("Can't open %s\n",s);
  447.     perror(s);
  448.     return;
  449.     }
  450.   if (interactive)
  451.      {
  452. /* store current file name so we can resume later */
  453.      if (!(recall.fp=strdup(curfile)))
  454.         {
  455.         printf("Out of memory\n");
  456.         fclose(ifile);
  457.         return;
  458.         }
  459. /* store position in current file and close it */
  460.      recall.pos=ftell(xci_infile);
  461.      fclose(xci_infile);
  462.      }
  463.   else
  464.      {
  465. /* no current file, so remember this handle but don't close it */
  466.      baseio=xci_infile;
  467.      recall.fp=NULL;
  468.      }
  469. /* add recall to linked list of nested files */
  470.   recall.next=instack;
  471.  
  472. /* make new file current */
  473.   strcpy(curfile,s);
  474.   xci_infile=ifile;
  475.  
  476. /* mark nesting level */
  477.   interactive++;
  478.  
  479. /* make recall the head of the fstack linked list */
  480.   instack=&recall;
  481.  
  482. /* call command recursively */
  483.   command(NULL,NULL,0,data,NULL);
  484.  
  485. /* close useless file */
  486.   fclose(xci_infile);
  487. /* restore old file */
  488.   if (instack->fp!=NULL)  /* is it a file? */
  489.      {
  490. /* open it */
  491.      xci_infile=fopen(instack->fp,"r");
  492.      if (!xci_infile)
  493.         {
  494. /* serious error! file vanished! reset to top level */
  495.         printf("Error opening %s\n",instack->fp);
  496.         xci_infile=baseio;
  497.         interactive=0;  /* bad error if nested */
  498.         }
  499.      else
  500.         {
  501. /* reposition old file */
  502.         fseek(xci_infile,instack->pos,SEEK_SET);
  503. /* make it current */
  504.         strcpy(curfile,instack->fp);
  505.         }
  506. /* release memory used for file name */
  507.      free(instack->fp);
  508.      }
  509.   else
  510.      {
  511. /* reset to console */
  512.      xci_infile=baseio;
  513.      }
  514. /* fix up linked list */
  515.   instack=instack->next;
  516.   interactive--;
  517.   }
  518.  
  519. /* Link a dll */
  520. XCICMD linkfunc(int cmd,char *s,struct udata *data)
  521.   {
  522.   if (cmd==2)
  523.     {
  524.     printf("Add user-defined commands\n");
  525.     return;
  526.     }
  527.   if (cmd==1||!s||!*s)
  528.      {
  529.      printf("Add user-defined commands via a DLL\n"
  530.        "Usage: link DLLNAME\n");
  531.      return;
  532.      }
  533.   if (adddll(s))
  534.     {
  535.     printf("Unable to load dll: %s\n",s);
  536.     }
  537.   }
  538.  
  539. /* Quit */
  540. XCICMD quitfunc(int cmd,char *s,struct udata *data)
  541.   {
  542.   if (cmd==0) { xci_exitflag=1; return; }
  543. /* long and short help message */
  544.   printf("Exits to DOS\n");
  545.   }
  546.  
  547. /* provide general help (scan from end to 0 call with cmd==2)
  548.    or specific help find command and call with cmd==1 */
  549. XCICMD helpfunc(int cmd,char *s,struct udata *data)
  550.   {
  551.   int i,j=0;
  552.   if (cmd==2) printf("Get help\n");
  553.   if (cmd==1) printf(
  554.   "Use the help command to learn about the available"
  555.   " commands\nUse HELP for a list of help topics"
  556.   " or \"HELP topic\""
  557.   " for help on a specific topic.\n");
  558.   if (cmd) return;
  559. /* call user's prehelp */
  560.   (*xcif_prehelp)();
  561. /* if specific command... */
  562.   if (s&&*s)
  563.     {
  564. /* find it and ask it about itself (command==1) */
  565.     i=findcmd(s);
  566.     if (i==-1) printf("No such command: %s\n",s);
  567.     else cmds[i].f(1,NULL,NULL);
  568.     }
  569.   else
  570. /* No specific command -- do them all (command==2) */
  571.     for (i=nrcmds-1;i>=0;i--)
  572.       {
  573.       char buf[22];
  574. /* might be a lot of commands -- pause on screenfulls */
  575.       if (!(++j%25))
  576.         {
  577.         printf("--More--");
  578.         j=0;
  579.         if (!getch()) getch();
  580.         putchar('\n');
  581.         }
  582. /* print header */
  583.       strncpy(buf,cmds[i].cmd,20);
  584.       strcat(buf,":");
  585.       printf("%-21.21s",buf);
  586. /* ask command for short help */
  587.       cmds[i].f(2,NULL,NULL);
  588.       }
  589. /* call user's post help */
  590.   (*xcif_posthelp)();
  591.   }
  592.  
  593.